﻿using FluentNHibernate.AutoMap;
using NHibernate;
using SimpleServiceBus.Persistence.Domain;
using SimpleServiceBus.Persistence.NHibernate.Mapping.AutoMapping;
using SimpleServiceBus.Persistence.NHibernate.Mapping.AutoMapping.Overrides;
using nhc = NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;


namespace SimpleServiceBus.Persistence.NHibernate
{
    public enum SchemaGenerationOptions
    {
        None = 0,
        UpdateSchema,
        CreateSchema
    }

    public class DataSessionManager : ISessionManager
    {
        public static ISessionManager Current { get; set; }

        public static void Create<T>() where T : ISessionStorage, new()
        {
            Create<T>(SchemaGenerationOptions.None);
        }

        public static void Create<T>(SchemaGenerationOptions schemaGenerationOptions)
            where T : ISessionStorage, new()
        {
            Current = new DataSessionManager(new T(),schemaGenerationOptions);
        }

        public static void Create(ISessionManager sessionManager)
        {
            Current = sessionManager;
        }

        private readonly ISessionStorage _currentSession;
        

        public DataSessionManager(ISessionStorage sessionStorage) : this(sessionStorage,SchemaGenerationOptions.None)
        {
        }

        public DataSessionManager(ISessionStorage sessionStorage,SchemaGenerationOptions schemaGenerationOptions)
        {
            nhc.Configuration cfg = Configure();
            
            if (schemaGenerationOptions == SchemaGenerationOptions.CreateSchema)
                CreateSchema(cfg);
            else if (schemaGenerationOptions == SchemaGenerationOptions.UpdateSchema)
                UpdateSchema(cfg);

            _sessionFactory = GetSessionFactory(cfg);
            _currentSession = sessionStorage;
        }
        
        private readonly ISessionFactory _sessionFactory;

        public ISession CurrentSession
        {
            get { return _currentSession.Session; }
            private set
            {
                _currentSession.Session = value;
            }
        }

        public ISession BeginSession()
        {
            if (CurrentSession != null)
                return RecycleCurrentSession();

           
            CurrentSession =  CreateSession() ;
            return CurrentSession;
        }

        public void EndSession()
        {
            if (CurrentSession != null)
            {
                CurrentSession.Flush();
                CurrentSession.Dispose();
                CurrentSession = null;
            }
        }

        public virtual ISession CreateSession()
        {
            return _sessionFactory.OpenSession();
        }

        public void FlushCurrentSession()
        {
            if (CurrentSession != null)
                CurrentSession.Flush();
        }

        public ISession RecycleCurrentSession()
        {
            EndSession();
            return BeginSession();
        }

        public virtual void CreateSchema()
        {
            CreateSchema(Configure());
        }

        public static void CreateSchema(nhc.Configuration cfg)
        {
            new SchemaExport(cfg).Create(true, true);
        }

        public virtual void UpdateSchema()
        {
            UpdateSchema(Configure());
        }

        public static void UpdateSchema(nhc.Configuration cfg)
        {
            new SchemaUpdate(cfg).Execute(true, true);
        }


        private static ISessionFactory GetSessionFactory(nhc.Configuration cfg)
        {
            return cfg.BuildSessionFactory();
        }

        protected virtual nhc.Configuration Configure()
        {
            nhc.Configuration cfg = new nhc.Configuration().Configure();
            var autoPersistenceModel = AutoPersistenceModel
                .MapEntitiesFromAssemblyOf<SsbEntity>()
                .Where(
                type =>
                type.IsClass
                && !type.IsAbstract
                && type.Namespace.StartsWith("SimpleServiceBus.Persistence.Domain")
                && type.Namespace.EndsWith("Entities")
                )
                .WithSetup(setup => setup.IsBaseType = type => type == typeof (SsbEntity))
                .ConventionDiscovery.Add<PrimaryKeyConvention>()
                .UseOverridesFromAssemblyOf<ManagedEndpointAutoMapOverride>();

            autoPersistenceModel.Configure(cfg);
            return cfg;
        }

        public void Dispose()
        {
            if (CurrentSession != null)
                CurrentSession.Dispose();
            CurrentSession = null;
            if(_sessionFactory != null)
                _sessionFactory.Dispose();
        }
    }
}